home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Resources / Chat & Communication / Digsby build 37 / digsby_setup.exe / lib / lxml / html / clean.pyo (.txt) < prev    next >
Python Compiled Bytecode  |  2008-10-13  |  14KB  |  579 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyo (Python 2.5)
  3.  
  4. import re
  5. import copy
  6.  
  7. try:
  8.     from urlparse import urlsplit
  9. except ImportError:
  10.     from urllib.parse import urlsplit
  11.  
  12. from lxml import etree
  13. from lxml.html import defs
  14. from lxml.html import fromstring, tostring, XHTML_NAMESPACE
  15. from lxml.html import _nons, _transform_result
  16.  
  17. try:
  18.     set
  19. except NameError:
  20.     from sets import Set as set
  21.  
  22.  
  23. try:
  24.     unichr = __builtins__['unichr']
  25. except (NameError, KeyError):
  26.     unichr = chr
  27.  
  28.  
  29. try:
  30.     unicode = __builtins__['unicode']
  31. except (NameError, KeyError):
  32.     unicode = str
  33.  
  34.  
  35. try:
  36.     bytes = __builtins__['bytes']
  37. except (NameError, KeyError):
  38.     bytes = str
  39.  
  40.  
  41. try:
  42.     basestring = __builtins__['basestring']
  43. except (NameError, KeyError):
  44.     basestring = (str, bytes)
  45.  
  46. __all__ = [
  47.     'clean_html',
  48.     'clean',
  49.     'Cleaner',
  50.     'autolink',
  51.     'autolink_html',
  52.     'word_break',
  53.     'word_break_html']
  54. _css_javascript_re = re.compile('expression\\s*\\(.*?\\)', re.S | re.I)
  55. _css_import_re = re.compile('@\\s*import', re.I)
  56. _javascript_scheme_re = re.compile('\\s*(?:javascript|jscript|livescript|vbscript|about|mocha):', re.I)
  57. _substitute_whitespace = re.compile('\\s+').sub
  58. _conditional_comment_re = re.compile('\\[if[\\s\\n\\r]+.*?][\\s\\n\\r]*>', re.I | re.S)
  59. _find_styled_elements = etree.XPath('descendant-or-self::*[@style]')
  60. _find_external_links = etree.XPath("descendant-or-self::a  [normalize-space(@href) and substring(normalize-space(@href),1,1) != '#'] |descendant-or-self::x:a[normalize-space(@href) and substring(normalize-space(@href),1,1) != '#']", namespaces = {
  61.     'x': XHTML_NAMESPACE })
  62.  
  63. class Cleaner(object):
  64.     scripts = True
  65.     javascript = True
  66.     comments = True
  67.     style = False
  68.     links = True
  69.     meta = True
  70.     page_structure = True
  71.     processing_instructions = True
  72.     embedded = True
  73.     frames = True
  74.     forms = True
  75.     annoying_tags = True
  76.     remove_tags = None
  77.     allow_tags = None
  78.     remove_unknown_tags = True
  79.     safe_attrs_only = True
  80.     add_nofollow = False
  81.     host_whitelist = ()
  82.     whitelist_tags = set([
  83.         'iframe',
  84.         'embed'])
  85.     
  86.     def __init__(self, **kw):
  87.         for name, value in kw.items():
  88.             if not hasattr(self, name):
  89.                 raise TypeError('Unknown parameter: %s=%r' % (name, value))
  90.             
  91.             setattr(self, name, value)
  92.         
  93.  
  94.     _tag_link_attrs = dict(script = 'src', link = 'href', applet = [
  95.         'code',
  96.         'object'], iframe = 'src', embed = 'src', layer = 'src', a = 'href')
  97.     
  98.     def __call__(self, doc):
  99.         if hasattr(doc, 'getroot'):
  100.             doc = doc.getroot()
  101.         
  102.         for el in doc.iter():
  103.             tag = el.tag
  104.             if isinstance(tag, basestring):
  105.                 el.tag = _nons(tag)
  106.                 continue
  107.         
  108.         for el in doc.iter('image'):
  109.             el.tag = 'img'
  110.         
  111.         if not self.comments:
  112.             self.kill_conditional_comments(doc)
  113.         
  114.         kill_tags = set()
  115.         if not self.remove_tags:
  116.             pass
  117.         remove_tags = set(())
  118.         if self.allow_tags:
  119.             allow_tags = set(self.allow_tags)
  120.         else:
  121.             allow_tags = set()
  122.         if self.scripts:
  123.             kill_tags.add('script')
  124.         
  125.         if self.safe_attrs_only:
  126.             safe_attrs = set(defs.safe_attrs)
  127.             for el in doc.iter():
  128.                 attrib = el.attrib
  129.                 for aname in attrib.keys():
  130.                     if aname not in safe_attrs:
  131.                         del attrib[aname]
  132.                         continue
  133.                 
  134.             
  135.         
  136.         if self.javascript:
  137.             if not self.safe_attrs_only:
  138.                 for el in doc.iter():
  139.                     attrib = el.attrib
  140.                     for aname in attrib.keys():
  141.                         if aname.startswith('on'):
  142.                             del attrib[aname]
  143.                             continue
  144.                     
  145.                 
  146.             
  147.             doc.rewrite_links(self._remove_javascript_link, resolve_base_href = False)
  148.             if not self.style:
  149.                 for el in _find_styled_elements(doc):
  150.                     old = el.get('style')
  151.                     new = _css_javascript_re.sub('', old)
  152.                     new = _css_import_re.sub('', old)
  153.                     if self._has_sneaky_javascript(new):
  154.                         del el.attrib['style']
  155.                         continue
  156.                     if new != old:
  157.                         el.set('style', new)
  158.                         continue
  159.                 
  160.                 for el in list(doc.iter('style')):
  161.                     if el.get('type', '').lower().strip() == 'text/javascript':
  162.                         el.drop_tree()
  163.                         continue
  164.                     
  165.                     if not el.text:
  166.                         pass
  167.                     old = ''
  168.                     new = _css_javascript_re.sub('', old)
  169.                     new = _css_import_re.sub('', old)
  170.                     if self._has_sneaky_javascript(new):
  171.                         el.text = '/* deleted */'
  172.                         continue
  173.                     if new != old:
  174.                         el.text = new
  175.                         continue
  176.                 
  177.             
  178.         
  179.         if self.comments or self.processing_instructions:
  180.             kill_tags.add(etree.Comment)
  181.         
  182.         if self.processing_instructions:
  183.             kill_tags.add(etree.ProcessingInstruction)
  184.         
  185.         if self.style:
  186.             kill_tags.add('style')
  187.             for el in _find_styled_elements(doc):
  188.                 del el.attrib['style']
  189.             
  190.         
  191.         if self.links:
  192.             kill_tags.add('link')
  193.         elif self.style or self.javascript:
  194.             for el in list(doc.iter('link')):
  195.                 if 'stylesheet' in el.get('rel', '').lower():
  196.                     el.drop_tree()
  197.                     continue
  198.             
  199.         
  200.         if self.meta:
  201.             kill_tags.add('meta')
  202.         
  203.         if self.page_structure:
  204.             remove_tags.update(('head', 'html', 'title'))
  205.         
  206.         if self.embedded:
  207.             for el in list(doc.iter('param')):
  208.                 found_parent = False
  209.                 parent = el.getparent()
  210.                 while parent is not None and parent.tag not in ('applet', 'object'):
  211.                     parent = parent.getparent()
  212.                 if parent is None:
  213.                     el.drop_tree()
  214.                     continue
  215.             
  216.             kill_tags.update(('applet',))
  217.             remove_tags.update(('iframe', 'embed', 'layer', 'object', 'param'))
  218.         
  219.         if self.frames:
  220.             kill_tags.update(defs.frame_tags)
  221.         
  222.         if self.forms:
  223.             remove_tags.add('form')
  224.             kill_tags.update(('button', 'input', 'select', 'textarea'))
  225.         
  226.         if self.annoying_tags:
  227.             remove_tags.update(('blink', 'marque'))
  228.         
  229.         _remove = []
  230.         _kill = []
  231.         for el in doc.iter():
  232.             if el.tag in kill_tags:
  233.                 if self.allow_element(el):
  234.                     continue
  235.                 
  236.                 _kill.append(el)
  237.                 continue
  238.             if el.tag in remove_tags:
  239.                 if self.allow_element(el):
  240.                     continue
  241.                 
  242.                 _remove.append(el)
  243.                 continue
  244.         
  245.         if _remove and _remove[0] == doc:
  246.             el = _remove.pop(0)
  247.             el.tag = 'div'
  248.             el.attrib.clear()
  249.         elif _kill and _kill[0] == doc:
  250.             el = _kill.pop(0)
  251.             if el.tag != 'html':
  252.                 el.tag = 'div'
  253.             
  254.             el.clear()
  255.         
  256.         for el in _kill:
  257.             el.drop_tree()
  258.         
  259.         for el in _remove:
  260.             el.drop_tag()
  261.         
  262.         allow_tags = self.allow_tags
  263.         if self.remove_unknown_tags:
  264.             if allow_tags:
  265.                 raise ValueError('It does not make sense to pass in both allow_tags and remove_unknown_tags')
  266.             
  267.             allow_tags = set(defs.tags)
  268.         
  269.         if allow_tags:
  270.             bad = []
  271.             for el in doc.iter():
  272.                 if el.tag not in allow_tags:
  273.                     bad.append(el)
  274.                     continue
  275.             
  276.             for el in bad:
  277.                 el.drop_tag()
  278.             
  279.         
  280.         if self.add_nofollow:
  281.             for el in _find_external_links(doc):
  282.                 if not self.allow_follow(el):
  283.                     el.set('rel', 'nofollow')
  284.                     continue
  285.             
  286.         
  287.  
  288.     
  289.     def allow_follow(self, anchor):
  290.         return False
  291.  
  292.     
  293.     def allow_element(self, el):
  294.         if el.tag not in self._tag_link_attrs:
  295.             return False
  296.         
  297.         attr = self._tag_link_attrs[el.tag]
  298.         if isinstance(attr, (list, tuple)):
  299.             for one_attr in attr:
  300.                 url = el.get(one_attr)
  301.                 if not url:
  302.                     return False
  303.                 
  304.                 if not self.allow_embedded_url(el, url):
  305.                     return False
  306.                     continue
  307.             
  308.             return True
  309.         else:
  310.             url = el.get(attr)
  311.             if not url:
  312.                 return False
  313.             
  314.             return self.allow_embedded_url(el, url)
  315.  
  316.     
  317.     def allow_embedded_url(self, el, url):
  318.         if self.whitelist_tags is not None and el.tag not in self.whitelist_tags:
  319.             return False
  320.         
  321.         (scheme, netloc, path, query, fragment) = urlsplit(url)
  322.         netloc = netloc.lower().split(':', 1)[0]
  323.         if scheme not in ('http', 'https'):
  324.             return False
  325.         
  326.         if netloc in self.host_whitelist:
  327.             return True
  328.         
  329.         return False
  330.  
  331.     
  332.     def kill_conditional_comments(self, doc):
  333.         bad = []
  334.         self._kill_elements(doc, (lambda el: _conditional_comment_re.search(el.text)), etree.Comment)
  335.  
  336.     
  337.     def _kill_elements(self, doc, condition, iterate = None):
  338.         bad = []
  339.         for el in doc.iter(iterate):
  340.             if condition(el):
  341.                 bad.append(el)
  342.                 continue
  343.         
  344.         for el in bad:
  345.             el.drop_tree()
  346.         
  347.  
  348.     
  349.     def _remove_javascript_link(self, link):
  350.         new = _substitute_whitespace('', link)
  351.         if _javascript_scheme_re.search(new):
  352.             return ''
  353.         
  354.         return link
  355.  
  356.     _substitute_comments = re.compile('/\\*.*?\\*/', re.S).sub
  357.     
  358.     def _has_sneaky_javascript(self, style):
  359.         style = self._substitute_comments('', style)
  360.         style = style.replace('\\', '')
  361.         style = _substitute_whitespace('', style)
  362.         style = style.lower()
  363.         if 'javascript:' in style:
  364.             return True
  365.         
  366.         if 'expression(' in style:
  367.             return True
  368.         
  369.         return False
  370.  
  371.     
  372.     def clean_html(self, html):
  373.         result_type = type(html)
  374.         if isinstance(html, basestring):
  375.             doc = fromstring(html)
  376.         else:
  377.             doc = copy.deepcopy(html)
  378.         self(doc)
  379.         return _transform_result(result_type, doc)
  380.  
  381.  
  382. clean = Cleaner()
  383. clean_html = clean.clean_html
  384. _link_regexes = [
  385.     re.compile('(?P<body>https?://(?P<host>[a-z0-9._-]+)(?:/[/\\-_.,a-z0-9%&?;=~]*)?)', re.I),
  386.     re.compile('mailto:(?P<body>[a-z0-9._-]+@(?P<host>[a-z0-9_._]+[a-z]))', re.I)]
  387. _avoid_elements = [
  388.     'textarea',
  389.     'pre',
  390.     'code',
  391.     'head',
  392.     'select',
  393.     'a']
  394. _avoid_hosts = [
  395.     re.compile('^localhost', re.I),
  396.     re.compile('\\bexample\\.(?:com|org|net)$', re.I),
  397.     re.compile('^127\\.0\\.0\\.1$')]
  398. _avoid_classes = [
  399.     'nolink']
  400.  
  401. def autolink(el, link_regexes = _link_regexes, avoid_elements = _avoid_elements, avoid_hosts = _avoid_hosts, avoid_classes = _avoid_classes):
  402.     if el.tag in avoid_elements:
  403.         return None
  404.     
  405.     class_name = el.get('class')
  406.     if class_name:
  407.         class_name = class_name.split()
  408.         for match_class in avoid_classes:
  409.             if match_class in class_name:
  410.                 return None
  411.                 continue
  412.         
  413.     
  414.     for child in list(el):
  415.         autolink(child, link_regexes = link_regexes, avoid_elements = avoid_elements, avoid_hosts = avoid_hosts, avoid_classes = avoid_classes)
  416.         if child.tail:
  417.             (text, tail_children) = _link_text(child.tail, link_regexes, avoid_hosts, factory = el.makeelement)
  418.             if tail_children:
  419.                 child.tail = text
  420.                 index = el.index(child)
  421.                 el[index + 1:index + 1] = tail_children
  422.             
  423.         tail_children
  424.     
  425.     if el.text:
  426.         (text, pre_children) = _link_text(el.text, link_regexes, avoid_hosts, factory = el.makeelement)
  427.         if pre_children:
  428.             el.text = text
  429.             el[:0] = pre_children
  430.         
  431.     
  432.  
  433.  
  434. def _link_text(text, link_regexes, avoid_hosts, factory):
  435.     leading_text = ''
  436.     links = []
  437.     last_pos = 0
  438.     while None:
  439.         (best_match, best_pos) = (None, None)
  440.         for regex in link_regexes:
  441.             regex_pos = last_pos
  442.             while None:
  443.                 match = regex.search(text, pos = regex_pos)
  444.                 if match is None:
  445.                     break
  446.                 
  447.                 host = match.group('host')
  448.                 for host_regex in avoid_hosts:
  449.                     if host_regex.search(host):
  450.                         regex_pos = match.end()
  451.                         break
  452.                         continue
  453.                 else:
  454.                     break
  455.                 continue
  456.                 if match is None:
  457.                     continue
  458.                 
  459.             if best_pos is None or match.start() < best_pos:
  460.                 best_match = match
  461.                 best_pos = match.start()
  462.                 continue
  463.         
  464.         if best_match is None:
  465.             if links:
  466.                 links[-1].tail = text
  467.             else:
  468.                 leading_text = text
  469.             break
  470.         
  471.         link = best_match.group(0)
  472.         end = best_match.end()
  473.         if link.endswith('.') or link.endswith(','):
  474.             end -= 1
  475.             link = link[:-1]
  476.         
  477.         prev_text = text[:best_match.start()]
  478.         if links:
  479.             links[-1].tail = prev_text
  480.         else:
  481.             leading_text = prev_text
  482.         anchor = factory('a')
  483.         body = best_match.group('body')
  484.         if not body:
  485.             body = link
  486.         
  487.         if body.endswith('.') or body.endswith(','):
  488.             body = body[:-1]
  489.         
  490.         anchor.text = body
  491.         links.append(anchor)
  492.         text = text[end:]
  493.         continue
  494.         return (leading_text, links)
  495.  
  496.  
  497. def autolink_html(html, *args, **kw):
  498.     result_type = type(html)
  499.     if isinstance(html, basestring):
  500.         doc = fromstring(html)
  501.     else:
  502.         doc = copy.deepcopy(html)
  503.     autolink(doc, *args, **kw)
  504.     return _transform_result(result_type, doc)
  505.  
  506. autolink_html.__doc__ = autolink.__doc__
  507. _avoid_word_break_elements = [
  508.     'pre',
  509.     'textarea',
  510.     'code']
  511. _avoid_word_break_classes = [
  512.     'nobreak']
  513.  
  514. def word_break(el, max_width = 40, avoid_elements = _avoid_word_break_elements, avoid_classes = _avoid_word_break_classes, break_character = unichr(8203)):
  515.     if el.tag in _avoid_word_break_elements:
  516.         return None
  517.     
  518.     class_name = el.get('class')
  519.     if class_name:
  520.         dont_break = False
  521.         class_name = class_name.split()
  522.         for avoid in avoid_classes:
  523.             if avoid in class_name:
  524.                 dont_break = True
  525.                 break
  526.                 continue
  527.         
  528.         if dont_break:
  529.             return None
  530.         
  531.     
  532.     if el.text:
  533.         el.text = _break_text(el.text, max_width, break_character)
  534.     
  535.     for child in el:
  536.         word_break(child, max_width = max_width, avoid_elements = avoid_elements, avoid_classes = avoid_classes, break_character = break_character)
  537.         if child.tail:
  538.             child.tail = _break_text(child.tail, max_width, break_character)
  539.             continue
  540.     
  541.  
  542.  
  543. def word_break_html(html, *args, **kw):
  544.     result_type = type(html)
  545.     doc = fromstring(html)
  546.     word_break(doc, *args, **kw)
  547.     return _transform_result(result_type, doc)
  548.  
  549.  
  550. def _break_text(text, max_width, break_character):
  551.     words = text.split()
  552.     for word in words:
  553.         if len(word) > max_width:
  554.             replacement = _insert_break(word, max_width, break_character)
  555.             text = text.replace(word, replacement)
  556.             continue
  557.     
  558.     return text
  559.  
  560. _break_prefer_re = re.compile('[^a-z]', re.I)
  561.  
  562. def _insert_break(word, width, break_character):
  563.     orig_word = word
  564.     result = ''
  565.     while len(word) > width:
  566.         start = word[:width]
  567.         breaks = list(_break_prefer_re.finditer(start))
  568.         if breaks:
  569.             last_break = breaks[-1]
  570.             if last_break.end() > width - 10:
  571.                 start = word[:last_break.end()]
  572.             
  573.         
  574.         result += start + break_character
  575.         word = word[len(start):]
  576.     result += word
  577.     return result
  578.  
  579.